package net.goutalk.glcs.module.system.controller;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import net.goutalk.glcs.common.annotation.GlcsLog;
import net.goutalk.glcs.common.constant.GlobalConstant;
import net.goutalk.glcs.common.enums.EnabledMark;
import net.goutalk.glcs.common.model.result.R;
import net.goutalk.glcs.common.sms.SmsSender;
import net.goutalk.glcs.common.utils.RedisUtil;
import net.goutalk.glcs.config.KeyCloakConfig;
import net.goutalk.glcs.config.GlcsSmsConfig;
import net.goutalk.glcs.module.organization.entity.User;
import net.goutalk.glcs.module.organization.service.IUserService;
import net.goutalk.glcs.module.system.dto.*;
import net.goutalk.glcs.module.system.service.ILoginService;
import net.goutalk.glcs.module.system.service.IOauthService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.keycloak.authorization.client.AuthzClient;
import org.keycloak.authorization.client.Configuration;
import org.keycloak.representations.AccessTokenResponse;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 登录控制器
 * </p>
 *
 * @author tanyujie
 * @since 2022-03-02
 */
@Api(tags = "登录模块")
@RestController
@RequestMapping(GlobalConstant.SYSTEM_MODULE_PREFIX)
@AllArgsConstructor
public class LoginController {

    private final ILoginService loginService;
    private final IUserService userService;

    private final RedisUtil redisUtil;

    private KeyCloakConfig keyCloakConfig;

    private SmsSender smsSender;

    private GlcsSmsConfig smsConfig;

    private IOauthService oauthService;


    @PostMapping("/login")
    @ApiOperation(value = "登录", notes = "传入账号:account,密码:password")
    @GlcsLog(value = "账号密码登录成功")
    public R login(@RequestBody @Valid LoginDto dto) {

        return R.ok(loginService.login(dto));
    }

    @PostMapping("/create-token")
    @ApiOperation(value = "创建token", notes = "传入账号:account,密码:password")
    @GlcsLog(value = "账号密码登录成功")
    public R createToken(@RequestBody @Valid CreateTokenDto dto) {

        return R.ok(loginService.createToken(dto));
    }


    /**
     * 发送验证码
     */
    @GetMapping("/captcha")
    @GlcsLog(value = "发送验证码")
    @ApiOperation(value = "发送验证码", notes = "传入账号:mobile")
    public R captcha(@RequestParam String mobile) {
        // 验证短信限时发送次数验证
        // 获取该号码短信的发送次数
        Object o = redisUtil.get(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile);
        int sendCount = NumberUtils.toInt(String.valueOf(o));
        if (o == null) {
            // 未发送过验证码，初始化
            redisUtil.set(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile, 1, smsConfig.getLimitTime() * 3600);
        } else if (sendCount > smsConfig.getLimitCount()) {
            // 发送次数超过限制
            return R.error("发送验证码次数达上限，请稍后再试！");
        } else {
            // 更新发送次数
            long expire = redisUtil.getExpire(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile);
            redisUtil.set(GlobalConstant.CACHE_COUNT_SMS_CODE_PREFIX + mobile, sendCount + 1, expire);
        }
        //生成六位数的字符串
        String code = RandomUtils.nextInt(100000, 999999) + StringPool.EMPTY;

        smsSender.sendCaptcha(mobile, code);

        redisUtil.set(GlobalConstant.CAPTCHA + StringPool.UNDERSCORE + mobile, code, 60L);
        return R.ok(Boolean.TRUE);
    }

    /**
     * 验证码
     */
    @PostMapping("/captcha")
    @GlcsLog(value = "验证码登录")
    public R loginByCaptcha(@RequestBody LoginCaptchaDto loginCaptchaDto) {
        String mobile = loginCaptchaDto.getMobile();
        String code = redisUtil.get(GlobalConstant.CAPTCHA + StringPool.UNDERSCORE + mobile);
        if (code != null && StringUtils.equals(code, loginCaptchaDto.getCode())) {
            User user = userService.getOne(Wrappers.<User>lambdaQuery().eq(User::getMobile, mobile), false);
            if (user == null) {
                return R.error("用户不存在！");
            }
            if (user.getEnabledMark() == EnabledMark.DISABLED.getCode()) {
                return R.error("账户未启用");
            }
            //此登录接口登录web端
            StpUtil.login(user.getId(), "PC");

            SaSession tokenSession = StpUtil.getTokenSession();
            tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, user);

            Map<String, Object> vo = new HashMap<>(1);
            vo.put(GlobalConstant.TOKEN_KEY, StpUtil.getTokenValue());

            return R.ok(vo);
        }
        return R.error("验证码不存在！");
    }


    /**
     * 退出
     */
    @PostMapping("/logout")
    public R logout() {
        StpUtil.logout();
        return R.ok("登出成功！");
    }

    @PostMapping("/token")
    @ApiOperation(value = "根据keycloak-token 登录", notes = "传入keycloak-token")
    @GlcsLog(value = "keycloak-token登录成功")
    public R loginByToken(@RequestBody KeyCloakLoginInfoDto dto) {

        Map<String, Object> credentialsMap = new HashMap<>(1);
        credentialsMap.put("secret", keyCloakConfig.getSecret());
        Configuration configuration = new Configuration(keyCloakConfig.getUrl(), keyCloakConfig.getRealm(), keyCloakConfig.getClientId(), credentialsMap, null);
        AuthzClient authzClient = AuthzClient.create(configuration);
        AccessTokenResponse response = authzClient.obtainAccessToken(keyCloakConfig.getUserName(), keyCloakConfig.getPassword());

        if (StrUtil.isNotBlank(response.getError())) {
            return R.error(response.getError());
        }
        //TODO keycloak 登陆过 解析token获取数据 做框架登录操作
        JWT jwt = JWTUtil.parseToken(dto.getToken());
        Object code = jwt.getPayload(keyCloakConfig.getPayload());

        User user = userService.getOne(Wrappers.lambdaQuery(User.class).eq(User::getCode, code));

        if (user == null) {
            return R.error("帐号密码错误！");
        } else if (!Integer.valueOf(1).equals(user.getEnabledMark())) {
            return R.error("该账号已被禁用！");
        }

        //此登录接口登录web端
        StpUtil.login(user.getId(), dto.getDevice());

        SaSession tokenSession = StpUtil.getTokenSession();
        tokenSession.set(GlobalConstant.LOGIN_USER_INFO_KEY, user);

        Map<String, Object> vo = new HashMap<>(1);
        vo.put(GlobalConstant.TOKEN_KEY, StpUtil.getTokenValue());

        return R.ok("登录成功！", vo);


    }

    @PostMapping("/qrcode-login")
    @ApiOperation(value = "oauth 扫码登录", notes = "oauth 扫码登录")
    @GlcsLog(value = "oauth 扫码登录")
    public R createAuthorizeUrl(@Valid @RequestBody CreateAuthorizeUrlDto dto){

        AuthRequest authRequest = oauthService.getAuthRequest(dto.getSource());
        String authorizeUrl = authRequest.authorize(AuthStateUtils.createState());
        return R.ok(authorizeUrl);
    }
}
